/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.loaders; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.io.IOException; import java.util.*; import org.openide.*; import org.openide.filesystems.*; import org.openide.cookies.InstanceCookie; import org.openide.util.Task; import org.openide.util.WeakListener; import org.openide.util.Mutex; import org.openide.util.RequestProcessor; /** Support class for creation of an object from the content * of a folder. * It implements <code>InstanceCookie</code>, so it * can be used as a cookie for a node or data object. * <P> * When created on a folder and started by invoking run method, * it scans its content (in a separate * thread) and creates a list of instances from which the new * instance of this object should be composed. The object * automatically listens to changes of components * in the folder, and if some change occurs, it allows the subclass to create * a new object. * * @author Jaroslav Tulach */ public abstract class FolderInstance extends Task implements InstanceCookie { /** a queue to run requests in */ // private static final RequestProcessor PROCESSOR = new RequestProcessor (); /** Folder to work with. */ protected DataFolder folder; /** map of primary file to their cookies (FileObject, HoldInstance) * @associates InstanceCookie*/ private HashMap map = new HashMap (17); /** object for this cookie. Either the right instance of object or * an instance of IOException or ClassNotFoundException */ private Object object; /** Listener and runner for this object */ private Listener listener; /** Create new folder instance. * @param df data folder to create instances from */ public FolderInstance (DataFolder df) { this (df, new Listener ()); } /** Constructor */ private FolderInstance (DataFolder df, Listener l) { super (l); listener = l; // link with listener l.folderInstance = this; folder = df; folder.addPropertyChangeListener ( WeakListener.propertyChange (listener, folder) ); } /** Full name of the data folder's primary file, separated by periods. * @return the name */ public String instanceName () { return folder.getPrimaryFile ().getPackageName ('.'); } /* Returns the root class of all objects. * Supposed to be overriden in subclasses. * * @return Object.class * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Class instanceClass () throws java.io.IOException, ClassNotFoundException { Object object = this.object; if (object != null) { if (object instanceof java.io.IOException) { throw (java.io.IOException)object; } if (object instanceof ClassNotFoundException) { throw (ClassNotFoundException)object; } return object.getClass (); } return Object.class; } /* * @return an object to work with * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Object instanceCreate () throws java.io.IOException, ClassNotFoundException { instanceFinished (); Object object = FolderInstance.this.object; if (object instanceof java.io.IOException) { throw (java.io.IOException)object; } if (object instanceof ClassNotFoundException) { throw (ClassNotFoundException)object; } return object; } /** Wait for instance initialization to finish. */ public final void instanceFinished () { waitFinished (); } /** Allows subclasses to decide whether they want to work with the specified * <code>DataObject</code> or not. * * @param dob a <code>DataObject</code> to test * @return the cookie for the <code>DataObject</code> or <code>null</code> * if it should not be used */ protected InstanceCookie acceptDataObject(DataObject dob) { InstanceCookie cookie; if (dob instanceof DataFolder) { cookie = acceptFolder((DataFolder)dob); } else { // test if we accept the instance cookie = (InstanceCookie)dob.getCookie (InstanceCookie.class); try { cookie = cookie == null ? null : acceptCookie (cookie); } catch (IOException ex) { // an error during a call to acceptCookie cookie = null; } catch (ClassNotFoundException ex) { // an error during a call to acceptCookie cookie = null; } } return cookie; } /** Allows subclasses to decide whether they want to work with * the specified <code>InstanceCookie</code> or not. * <p>The default implementation simply * returns the same cookie, but subclasses may * decide to return <code>null</code> or a different cookie. * * @param cookie the instance cookie to test * @return the cookie to use or <code>null</code> if this cookie should not * be used * @exception IOException if an I/O error occurred calling a cookie method * @exception ClassNotFoundException if a class is not found in a call to a cookie method */ protected InstanceCookie acceptCookie (InstanceCookie cookie) throws java.io.IOException, ClassNotFoundException { return cookie; } /** Allows subclasses to decide how they want to work with a * provided folder. * The default implementation simply returns <code>null</code> to * indicate that folders should not be recursed, * but subclasses can do otherwise. * * @param df data folder to create cookie for * @return the cookie for this folder or <code>null</code> if this folder should not * be used */ protected InstanceCookie acceptFolder (DataFolder df) { return null; } /** Notifies subclasses that the set of cookies for this folder * has changed. * A new object representing the folder should * be created (or the old one updated). * Called both upon initialization of the class, and change of its cookies. * * @param cookies updated array of instance cookies for the folder * @return object to represent these cookies * * @exception IOException an I/O error occured * @exception ClassNotFoundException a class has not been found */ protected abstract Object createInstance (InstanceCookie[] cookies) throws java.io.IOException, ClassNotFoundException; /** Starts recreation of the instance in special thread. */ public void recreate () { // PROCESSOR.post (listener, 0, Thread.NORM_PRIORITY + 2); Mutex.EVENT.writeAccess (listener); // run (); } /** Starts recreation of the instance immediatelly * in current thread. */ public void run () { recreateInstance (); } /** Recreates the instance. */ final void recreateInstance () { HashSet toRemove = new HashSet (map.keySet ()); ArrayList cookies = new ArrayList (); DataObject[] list = folder.getChildren (); int size = list.length; for (int i = 0; i < size; i++) { // testing InstanceCookie cookie = acceptDataObject(list[i]); if (cookie != null) { // cookie accepted FileObject fo = list[i].getPrimaryFile (); if (!toRemove.remove (fo)) { // such cookie is not there yet InstanceCookie hold = new HoldInstance (cookie); map.put (fo, hold); cookies.add (hold); } else { // old cookie, already there => only add it to the list of cookies cookies.add (map.get (fo)); } } } // now remove the cookies that are no longer in the folder Iterator it = toRemove.iterator (); while (it.hasNext ()) { map.remove (it.next ()); } // create the list of cookies final InstanceCookie[] all = new InstanceCookie[cookies.size ()]; cookies.toArray (all); try { object = createInstance (all); } catch (IOException ex) { object = ex; } catch (ClassNotFoundException ex) { object = ex; } finally { notifyFinishedHelper (); } } /** Helper to provide access to notifyFinished mehtod for innerclasses of this class */ private void notifyFinishedHelper () { notifyFinished (); } /** Listener on change of folder's children and a starter for the task. */ private static class Listener implements PropertyChangeListener, Runnable { /** reference to FolderInstance */ FolderInstance folderInstance; /** Watching children */ public void propertyChange (PropertyChangeEvent ev) { if (DataFolder.PROP_CHILDREN.equals (ev.getPropertyName ())) { folderInstance.recreate (); } } /** Start the recreation process. */ public void run () { folderInstance.recreateInstance (); } } /** A instance cookie that holds the result of first * invocation of the provided cookie. * * PENDING: In future it could watch over provided FileObject if it * changes and if so, start the recreation. */ private final static class HoldInstance extends Object implements InstanceCookie { /** the cookie to delegate to */ private InstanceCookie cookie; /** the result or an exception */ private Object object; public HoldInstance (InstanceCookie cookie) { this.cookie = cookie; } /** Full name of the data folder's primary file separated by dots. * @return the name */ public String instanceName () { return cookie.instanceName (); } /** Returns the root class of all objects. * Supposed to be overriden in subclasses. * * @return Object.class * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Class instanceClass () throws java.io.IOException, ClassNotFoundException { if (object instanceof IOException) throw (IOException)object; if (object instanceof ClassNotFoundException) throw (ClassNotFoundException)object; if (object == null) { // delegate return cookie.instanceClass (); } else { // return the object class return object.getClass (); } } /** * @return an object to work with * @exception IOException an I/O error occured * @exception ClassNotFoundException the class has not been found */ public Object instanceCreate () throws java.io.IOException, ClassNotFoundException { if (object instanceof java.io.IOException) { throw (java.io.IOException)object; } if (object instanceof ClassNotFoundException) { throw (ClassNotFoundException)object; } // create the object if not yet created if (object == null) { object = cookie.instanceCreate (); } return object; } } } /* * Log * 16 Gandalf 1.15 11/5/99 Jaroslav Tulach WeakListener has now * registration methods. * 15 Gandalf 1.14 11/3/99 Jaroslav Tulach Does not use event queue. * 14 Gandalf 1.13 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 13 Gandalf 1.12 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 12 Gandalf 1.11 5/11/99 Ian Formanek Fixed to compile * 11 Gandalf 1.10 5/11/99 Jaroslav Tulach ToolbarPool changed to * look better in Open API * 10 Gandalf 1.9 5/4/99 Jaroslav Tulach Updates on change of * files. * 9 Gandalf 1.8 4/24/99 Jaroslav Tulach * 8 Gandalf 1.7 3/30/99 Ian Formanek FolderInstance creation * in single thread * 7 Gandalf 1.6 3/10/99 Jesse Glick [JavaDoc] * 6 Gandalf 1.5 3/2/99 David Simonek icons repair * 5 Gandalf 1.4 2/26/99 David Simonek * 4 Gandalf 1.3 2/19/99 David Simonek menu related changes... * 3 Gandalf 1.2 2/1/99 Jesse Glick [JavaDoc] * acceptDataObject() does not throw anything. * 2 Gandalf 1.1 1/20/99 David Peroutka +acceptDataObject * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ */